/**
 * LinkedLST, Kellen Donohue
 * May 5, 2011
 * RailsModel.java - Defines a representation of a rails model used to create 
 * the Java objects
 */
package edu.washington.cs.labspecimentransport.data;

import java.io.IOException;
import java.io.PrintStream;
import java.util.Calendar;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Scanner;

/**
 * Internal representation of what a rails model looks like. Corresponds to a
 * single java object
 * 
 * @author Kellen
 * 
 */
@SuppressWarnings("rawtypes")
public class RailsModel {
	/**
	 * Map from field name to Type
	 */
	Map<String, Class> fields;

	/**
	 * Name of the model, will become the name of the object
	 */
	String name;

	/**
	 * Create a new model of the given name
	 * 
	 * @param name
	 *            name of the model
	 */
	public RailsModel(String name) {
		this.name = RailsModel.javaifyName(name);
		this.fields = new HashMap<String, Class>();
	}

	/**
	 * Writes the model to disk as a Java object
	 * 
	 * @param path
	 *            Path to write the file to, not including the name of the file
	 * @throws IOException
	 *             If the file can't be created
	 */
	public void writeModel(String path) throws IOException {
		PrintStream writer = new PrintStream(path + this.name + ".java");

		this.writeComments(writer);
		writer.println("package edu.washington.cs.labspecimentransport.dataobjects;");
		writer.println();

		this.writeOpeningAndConstructor(writer);

		this.writeFields(writer);
		writer.println();
		this.writeAccessors(writer);

		this.writeClosing(writer);
		writer.close();
	}

	/**
	 * Write each of the fields of the current model
	 * 
	 * @param writer
	 *            Printer to write with
	 */
	private void writeFields(PrintStream writer) {
		for (Entry<String, Class> x : this.fields.entrySet()) {
			writer.println("    private " + x.getValue().getName() + " "
					+ x.getKey() + ";");
		}
	}

	/**
	 * Write accessors for each field of the current model
	 * 
	 * @param writer
	 *            Printer to write with
	 */
	private void writeAccessors(PrintStream writer) {
		for (Entry<String, Class> x : this.fields.entrySet()) {
			// Getter
			// TODO: Change to java syntax
			writer.println("    public " + x.getValue().getName() + " get"
					+ x.getKey().substring(0, 1).toUpperCase()
					+ x.getKey().substring(1) + "() {");
			writer.println("        return this." + x.getKey() + ";");
			writer.println("    }");
			writer.println();

			// Setter
			// TODO: Change to java syntax
			writer.println("    public void set_" + x.getKey() + "("
					+ x.getValue().getName() + " val) {");
			writer.println("        this." + x.getKey() + " = val;");
			writer.println("    }");
			writer.println();
		}
	}

	/**
	 * Write the end of the class
	 * 
	 * @param writer
	 *            Writer to write with
	 */
	private void writeClosing(PrintStream writer) {
		writer.println("}");
	}

	/**
	 * Write the opening of the class and the constructor
	 * 
	 * @param writer
	 *            Writer to print with
	 */
	private void writeOpeningAndConstructor(PrintStream writer) {
		if (name.equals("Item")) {
			writer.println("public class " + this.name
					+ " implements java.io.Serializable {");
		} else {
			writer.println("public class " + this.name + " {");
		}
	}

	/**
	 * Writer the comments at the top of the file, warn viewers not to edit
	 * 
	 * @param writer
	 *            Writer to print with
	 */
	private void writeComments(PrintStream writer) {
		writer.println("/**");
		writer.println(" * LinkedLST, Auto-generated file");
		Calendar cal = Calendar.getInstance();
		writer.println(" * Generated on " + cal.getTime());
		writer.println(" * Matches rails model named " + this.name);
		writer.println(" * DON'T EDIT THIS AUTO-GENERATED FILE");
		writer.println(" * YOUR CHANGES WILL BE ERASED BY THE BUILD SCRIPT");
		writer.println(" * If you want to make changes here, talk to Zach)");
		writer.println(" */");
		writer.println();
	}

	/**
	 * Convert a ruby name to a java name
	 * 
	 * @param rubyName
	 *            The ruby name with underscore_spacing
	 * @return A java name with camelCase
	 */
	public static String javaifyName(String rubyName) {
		StringBuilder builder = new StringBuilder();
		Scanner scanner = new Scanner(rubyName);
		scanner.useDelimiter("_");
		while (scanner.hasNext()) {
			String nextWord = scanner.next();
			builder.append(String.valueOf(nextWord.charAt(0)).toUpperCase());
			builder.append(String.valueOf(nextWord.substring(1)));
		}
		return builder.toString();
	}
}
